package com.example.datachangerecord.record.processor;


import com.example.datachangerecord.mapper.BeRecordLogMapper;
import com.example.datachangerecord.record.annotation.HelpCol;
import com.example.datachangerecord.record.annotation.Record;
import com.example.datachangerecord.record.annotation.Records;
import com.example.datachangerecord.record.annotation.SelectCol;
import com.example.datachangerecord.record.emun.ColType;
import com.example.datachangerecord.record.emun.RecordTable;
import com.example.datachangerecord.record.emun.SelectType;
import com.example.datachangerecord.record.eneity.ColumnData;
import com.example.datachangerecord.record.eneity.ContextRecordData;
import com.example.datachangerecord.record.eneity.Table;
import com.example.datachangerecord.service.BeRecordLogService;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 主要写公共方法位置
 *
 * @author xyj
 */
public abstract class AbstractRecordProcessor implements RecordProcessor {

    @Autowired
    protected BeRecordLogMapper mapper;

    @Autowired
    protected BeRecordLogService beRecordLogService;


    /**
     * 缓存table信息,用于之后使用不再解析一次
     * key 是，类+方法名+入参+table,没用到
     */
    protected final Map<String, Table> tableObjects = new ConcurrentHashMap<>(256);


    @Override
    public List<? extends ContextRecordData> beforeProceed(Method method, Object[] args) {

        //解析方法
        List<ContextRecordData> perData = parseMethod(method, args);

        //查询数据库并填充修改前数据
        fillPerData(perData);

        //自定义设置
        return customPerData(perData);

    }

    private void fillPerData(List<ContextRecordData> perData) {
        perData.forEach(e -> {
            List<LinkedHashMap<String, Object>> linkedHashMaps = queryData(e);
            e.setPerData(linkedHashMaps);
        });
    }

    private List<LinkedHashMap<String, Object>> queryData(ContextRecordData data) {
        //查询数据库
        Table table = data.getTable();

        String tableName = table.getName();

        StringBuilder conditionSql = new StringBuilder(" 1=1 ");

        if (table.getSelectType() == SelectType.LOGIC) {
            String logicName = table.getLogicName().name();
            conditionSql.append(" and ").append(logicName).append(" = 'N'");
        }

        List<ColumnData> selectCols = table.getSelectCols();
        for (ColumnData columnData : selectCols) {
            String colName = columnData.getColName();
            Object value = columnData.getValue();
            if (columnData.getColType() == ColType.NUMBER) {
                conditionSql.append(" and ").append(colName).append(" = ").append(value);
            }
            if (columnData.getColType() == ColType.STRING) {
                conditionSql.append(" and ").append(colName).append(" = ").append("'").append(value).append("'");
            }

        }


        return mapper.selectPerData(tableName, conditionSql.toString());

    }

    /**
     * 自定义装饰修改前数据
     */
    protected List<ContextRecordData> customPerData(List<ContextRecordData> perData) {
        return perData;
    }

    private List<ContextRecordData> parseMethod(Method method, Object[] args) {

        Record record = method.getAnnotation(Record.class);

        Records records = method.getAnnotation(Records.class);

        if (record != null) {
            return processRecord(method, args, record);
        }

        if (records != null) {
            return processRecords(method, args, records);
        }


        return new ArrayList<>();
    }

    private List<ContextRecordData> processRecords(Method method, Object[] args, Records records) {
        //todo
        return null;
    }

    private List<ContextRecordData> processRecord(Method method, Object[] args, Record record) {

        beforeProcessRecord(method, args, record);

        List<ContextRecordData> result = new ArrayList<>(1);
        Table table = null;

        //解析Record
        table = processRecord(record);


        //解析method
        processMethod(method, args, table);

        ContextRecordData contextRecordData = new ContextRecordData();
        contextRecordData.setTable(table);
        result.add(contextRecordData);
        return result;
    }

    protected void beforeProcessRecord(Method method, Object[] args, Record record) {
        //do nothing
    }

    private String getTableKey(Method method) {
        StringBuilder result = new StringBuilder();
        result.append(method.getName());
        result.append(method.getDeclaringClass().toString());
        Class<?>[] parameterTypes = method.getParameterTypes();
        for (Class<?> parameterType : parameterTypes) {
            result.append(parameterType.toString());
        }
        return result.toString();
    }

    private void processMethod(Method method, Object[] args, Table table) {
        List<ColumnData> selectCols = new LinkedList<>();
        List<ColumnData> helpCols = new LinkedList<>();

        //入参上的解析
        Annotation[][] parameterAnnotations = method.getParameterAnnotations();
        for (int i = 0; i < parameterAnnotations.length; i++) {
            Annotation[] parameterAnnotation = parameterAnnotations[i];
            for (Annotation value : parameterAnnotation) {
                Class<? extends Annotation> aClass = value.annotationType();
                if (aClass == HelpCol.class) {
                    HelpCol value1 = (HelpCol) value;
                    //生成colum
                    ColumnData col = makeColumnByAnnotation(value1, args, i);
                    helpCols.add(col);
                }
                if (aClass == SelectCol.class) {
                    SelectCol value1 = (SelectCol) value;
                    //生成colum
                    ColumnData col = makeColumnByAnnotation(value1, args, i);
                    selectCols.add(col);
                }
            }
        }
        Class<?>[] parameterTypes = method.getParameterTypes();
        //入参内的属性解析
        for (int i = 0; i < parameterTypes.length; i++) {
            Field[] fields = parameterTypes[i].getDeclaredFields();
            for (Field field : fields) {
                HelpCol annotation = field.getAnnotation(HelpCol.class);
                if (annotation != null) {
                    field.setAccessible(true);
                    ColumnData col = makeColumnByAnnotation(annotation, args[i], parameterTypes[i], field);
                    helpCols.add(col);
                }
                SelectCol annotation2 = field.getAnnotation(SelectCol.class);
                if (annotation2 != null) {
                    field.setAccessible(true);
                    ColumnData col = makeColumnByAnnotation(annotation2, args[i], parameterTypes[i], field);
                    selectCols.add(col);
                }
            }
        }
        table.setHelpCols(helpCols);
        table.setSelectCols(selectCols);
    }

    private Table processRecord(Record record) {
        String tableName;
        String tableDesc;
        RecordTable recordTable = record.value();


        tableName = "".equals(record.tableName()) ? recordTable.name() : record.tableName();
        tableDesc = "".equals(record.des()) ? recordTable.getDesc() : record.des();

        return Table.builder().name(tableName).remark(record.remark()).desc(tableDesc).selectType(record.selectType()).concernFields(record.concernFields()).logicName(record.logicName()).build();
    }

    @Override
    public void afterProceed(List<? extends ContextRecordData> context) {
        if (CollectionUtils.isEmpty(context)) {
            return;
        }
        afterCustom(context);
        // 查询修改后的数据
        context.forEach(e -> {
            List<LinkedHashMap<String, Object>> linkedHashMaps = queryData(e);
            e.setAfterData(linkedHashMaps);
        });

        //比较记录数据
        compareAndRecord(context);

    }

    protected void afterCustom(List<? extends ContextRecordData> context) {
    }

    protected void compareAndRecord(List<? extends ContextRecordData> context) {

    }


    @SneakyThrows
    private ColumnData makeColumnByAnnotation(HelpCol col, Object arg, Class<?> parameterType, Field field) {
        field.setAccessible(true);
        Object o = field.get(arg);
        Class<?> c = null;
        if (o != null) {
            c = o.getClass();
        }
        return ColumnData.builder().colName(col.value()).isSelect(false).tableName(col.tableName())

                .fieldClass(c).build();
    }

    @SneakyThrows
    private ColumnData makeColumnByAnnotation(SelectCol col, Object arg, Class<?> parameterType, Field field) {
        field.setAccessible(true);
        Object o = field.get(arg);
        Class<?> c = null;
        if (o != null) {
            c = o.getClass();
        }
        return ColumnData.builder().colName(col.value()).isSelect(true).tableName(col.tableName()).value(o).fieldClass(c).colType(col.colType()).build();
    }


    private ColumnData makeColumnByAnnotation(HelpCol col, Object[] args, int index) {
        Class<?> c = null;
        if (args[index] != null) {
            c = args[index].getClass();
        }
        return ColumnData.builder().colName(col.value()).isSelect(false).tableName(col.tableName())

                .fieldClass(c).build();
    }

    private ColumnData makeColumnByAnnotation(SelectCol col, Object[] args, int index) {
        Class<?> c = null;
        if (args[index] != null) {
            c = args[index].getClass();
        }
        return ColumnData.builder().colName(col.value()).isSelect(true).tableName(col.tableName()).value(args[index]).fieldClass(c).colType(col.colType()).build();
    }
}
